3

本文讲解内容

针对两类发奖需求的四种抽奖逻辑及细节

一般H5抽奖活动的发奖需求分为

1.一定中奖(奖品库存不空的情况下)
2.不一定中奖

发奖接口的最终实现要求

1.奖品不超发
2.唯一奖品单次发放
3.对并发有一定的限制

接口实战

1.根据奖品开放时间进行抽奖

public function award($openid)
    {
        $award = Award::find()->where(['openid' => ''])
            ->andWhere(['>', 'open_at', 0])->andWhere(['<', 'open_at', time()])
            ->orderBy('open_at ASC')->limit(1)->one();
        if (!empty($award)) {
            $res = Award::updateAll(
                [
                    'openid' => $openid
                ],
                'code = :code AND openid = :openid',
                [
                    ':code' => $award['code'],
                    ':openid' => ''
                ]
            );
            if ($res) {
                return ArrayHelper::toArray($award);
            }
        }
        return [];
    }

这种方式,多用户并发情况下,会出现多个用户相同奖品,由于update语句限制,拿到相同奖品码的用户中只有一人能中得奖品。

2.在开放时间的基础上加上类型概率

public function randAward($openid)
    {
        $number = rand(0, 100);
        $type = 5;
        if ($number < 10) {
            $type = 1;
        } else if ($number < 30) {
            $type = 2;
        } else if ($number < 70) {
            $type = 3;
        } else if ($number < 80) {
            $type = 4;
        }
        $award = Award::find()->where(['openid' => ''])
            ->andWhere(['>', 'open_at', 0])->andWhere(['<', 'open_at', time()])
            ->andWhere(['type' => $type])
            ->orderBy('open_at ASC')->limit(1)->one();
        if (!empty($award)) {
            $res = Award::updateAll(
                [
                    'openid' => $openid
                ],
                'code = :code AND openid = :openid',
                [
                    ':code' => $award['code'],
                    ':openid' => ''
                ]
            );
            if ($res) {
                return ArrayHelper::toArray($award);
            }
        }
        return [];
    }

这种方式,也会出现多个用户相同奖品,但加上type限制后,用户被分散在各个类型中,未中奖概率会比上面的例子低。

3.利用Redis奖品池的概念进行发奖

    public function redisAward($openid)
    {
        try {
            $redis = \Yii::$app->redis->client();
            $code = $redis->LPop(self::AWARD_LIST_KEY);
        } catch (Exception $err) {
            return [];
        }
        $res = Award::updateAll(
            [
                'openid' => $openid
            ],
            'code = :code AND openid = :openid',
            [
                ':code' => $code,
                ':openid' => ''
            ]
        );
        if ($res) {
            $award = Award::find()->where(['code' => $code])->limit(1)->one();
            return ArrayHelper::toArray($award);
        }
        return [];
    }

这种利用预先生成奖品池的方式,奖品池不空的情况下,每个用户都会取走不同奖品码,要注意的是 前期生成奖品池及后期操作奖品池时,防止奖品码复用

4.根据奖品开放时间(类型)进行抽奖,换成用sql语句进行发奖

public function sqlAward($openid)
    {
        $sql = "UPDATE award SET openid = :openid 
                WHERE open_at > 0 AND openid = '' AND open_at < :time
                ORDER BY open_at ASC LIMIT 1";
        $res = \Yii::$app->db->createCommand($sql, [':time' => time(), ':openid' => $openid])->execute();
        if ($res) {
            return Award::find()->where(['openid' => $openid])->limit(1)->asArray()->one();
        }
        return [];
    }

一定中奖需求下,建议采用Redis奖品池或者sql语句进行update
以上四种方式在多用户并发的情况下带来不一样的结果

除了多用户并发,还会出现恶刷情况,就是同一用户并发请求
这种情况应该在真正进入抽奖逻辑之前进行限制

可以根据实际需求搭配以下方式进行限制

public function actionAward()
    {
        $openid = 'okjkLj7-UL4gXknY9bDkFn0O6Jos';
        $redis = \Yii::$app->redis->client();
//            用户单次数
        if (!$redis->sAdd(self::USER_LIST_KEY, $openid)) {
            return [];
        }
        return $this->sqlAward($openid);
    }

也可以限制抽奖人数

public function actionAward()
    {
        $openid = CommonTool::randString(32);
        try {
            $redis = \Yii::$app->redis->client();
//            抽奖用户数量
            $list = $redis->sMembers(self::USER_LIST_KEY);
            if (count($list) > 1000) {
                return ;
            }
        } catch (Exception $err) {

        }
        $award = $this->sqlAward($openid);
    }

写在最后的最后

H5活动抽奖接口需要注意几点
1.检查用户有效性
2.限制单用户访问次数
3.使用概率让用户分流,从而控制真正进入抽奖逻辑的请求
4.记录抽奖领奖等相关操作的时间设备IP等..
5.控制奖品的分布(时间,插空,概率等)
6.做好索引关系


zhou
17 声望0 粉丝